<template lang="pug">
.container
  img#imgDemo(
    :src="originImg",
    @load="loadImg",
    :style="'left:' + -imgWidth + 'px;top:' + -imgHeight + 'px'"
    class="hideImage"
  )
  .main
    .header-content
      el-steps(align-center, :active="0", size="mini")
        el-step(title="点击选择图片", description="")
        el-step(title="在原图或生成图上点击选择颜色", description="")
        el-step(title="执行替换/清除颜色", description="")
        el-step(title="点击下载图片保存到本地", description="")

    .canvas-content
      el-col.origin-box(
        ref="originBox",
        :xs="24",
        :sm="24",
        :md="12",
        :lg="{ span: 8, offset: 2 }",
        :xl="{ span: 8, offset: 2 }"
      )
        el-tag(type="danger", size="mini") 原图
        .origin-canvas-content(
          ref="originCanvasContent",
          :style="'width:' + canWidth + 'px'"
        )
          canvas#beforeCan(
            ref="originCan",
            :width="canWidth",
            :height="canHeight"
          )
          canvas#layer(
            ref="layerCan",
            :width="canWidth",
            :height="canHeight",
            @click="(e) => pickPoint(e, 'origin')"
          )
          el-upload.avatar-uploader(
            ref="logoUpload",
            accept="image/*",
            action="#",
            :auto-upload="false",
            :show-file-list="false",
            :on-change="handleStatusChange"
          )
            el-button(size="small", type="primary") 选择图片

      el-col.operate-list-box(:xs="24", :sm="24", :md="12", :lg="4", :xl="4")
        .removeColor.operate-item
          h3.header 清除颜色：
          el-button(@click="handleTrans('remove')", :disabled="!fromColor") 清除

        .replace-color.operate-item
          h3.header 颜色替换：
          .replace-color-picker-box
            el-color-picker(
              v-model="fromColor",
              color-format="rgb",
              :show-alpha="true"
            )
            el-button.replace-btn(
              @click="() => handleTrans()",
              :disabled="!fromColor"
            ) 替换
            el-color-picker(
              v-model="toColor",
              color-format="rgb",
              :show-alpha="false"
            )
            el-tooltip(:content="(strawPoint ? '关闭' : '开启') + '吸管工具'")
              el-button.straw(
                type="text",
                :class="strawPoint ? 'active' : ''",
                @click="strawPoint = !strawPoint"
              )
                i.iconfont.icon-xiguan

        .tolerance.operate-item
          h3.header 
            span 容差：
            span.tooltip 容差表示匹配的精确度，容差越大匹配的准确度越低
          .body
            el-slider(v-model="tolerance", :marks="marks")

        .more-tools.operate-item
          h3.header 
            span 更多工具：
            span.tooltip 开发中...
          el-button(
            size="mini",
            :type="moreTool === 'pencel' ? 'primary' : ''",
            round,
            autofocus,
            icon="iconfont icon-huabi1",
            @click="moreTool = moreTool === 'pencel' ? '' : 'pencel'",
            disabled
          ) 画笔

      el-col.trans-box(:xs="24", :sm="24", :md="12", :lg="8", :xl="8")
        el-tag(type="success", size="mini") 生成图
        .trans-canvas-content(
          ref="transCanvasContent",
          :style="'width:' + canWidth + 'px'"
        )
          canvas#transCan(
            ref="transCan",
            :width="canWidth",
            :height="canHeight",
            @click="(e) => pickPoint(e, 'trans')"
          )
          .box
            el-button(size="small", type="primary", @click="downloadPng") 下载图片
            el-button(size="small", type="primary", @click="resetImgData") 重置
            el-button(
              size="small",
              type="primary",
              @click="undo",
              :disabled="disUndo"
            ) 后退
            el-button(
              size="small",
              type="primary",
              @click="redo",
              :disabled="!index"
            ) 前进

    .example-content
      span.label 没有图片？试试这些图片：
      .img-content
        img(
          v-for="item in exampleImg",
          :src="item",
          height="50",
          @click="uploadImg(item)"
        )
</template>

<script>
const white = require("../assets/white.png");
const pig = require("../assets/pig.png");
const logo = require("../assets/logo.png");
const girl = require("../assets/girl.jpeg");
import { postPoint } from "../utils";
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    return {
      canWidth: 400,
      canHeight: 400,
      originCtx: null,
      layerCtx: null,
      transCtx: null,
      selectedColor: { from: [], to: [] },
      imgWidth: 400,
      imgHeight: 400,
      imageData: {},
      originImg: "",
      loadFileName: "",
      imgStock: [],
      index: 0,
      tolerance: 10,
      marks: {
        0: "0",
        10: "10",
        50: "50",
        100: "100",
      },
      strawPoint: false,
      fileName: "download",
      exampleImg: [white, pig, logo, girl],
      moreTool: "",
    };
  },
  computed: {
    fromColor: {
      get() {
        let colorArr = [],
          res = "";
        if (Array.isArray(this.selectedColor.from)) {
          colorArr = this.selectedColor.from.map((e, index) => {
            return index === 3 ? e / 255 : e;
          });
          res = colorArr.length ? "rgba(" + colorArr.join(",") + ")" : null;
        } else {
          res = this.selectedColor.from + "";
        }
        return res;
      },
      set(val) {
        this.selectedColor.from = val;
      },
    },
    toColor: {
      get() {
        let colorArr = [],
          res = "";
        if (Array.isArray(this.selectedColor.to)) {
          colorArr = this.selectedColor.to.map((e, index) => {
            return index === 3 ? e / 255 : e;
          });
          res = colorArr.length
            ? "rgba(" + colorArr.join(",") + ")"
            : "rgb(255, 255, 255)";
        } else {
          res = this.selectedColor.to + "";
        }
        return res;
      },
      set(val) {
        this.selectedColor.to = val;
      },
    },
    transColor() {
      const colorArr = this.toColor.split(/\(|\)/)[1].split(",");
      return colorArr.length === 3 ? [...colorArr, "1"] : colorArr;
    },
    disUndo() {
      return this.index >= this.imgStock.length - 1;
    },
  },
  mounted() {
    try {
      postPoint(); //埋点
    } catch (error) {
      console.log(error);
    }

    this.$nextTick(() => {
      const contentWidth = document.querySelector(".origin-box").offsetWidth;
      this.canWidth = Math.min(contentWidth - 12, this.canWidth);

      this.originCtx = this.$refs.originCan.getContext("2d");
      this.layerCtx = this.$refs.layerCan.getContext("2d");
      this.transCtx = this.$refs.transCan.getContext("2d");
    });
  },
  methods: {
    // 图片加载完成
    loadImg(e) {
      postPoint(); //埋点
      const img = e.target;
      const width = img.offsetWidth;
      const height = img.offsetHeight;
      this.imgWidth = width;
      this.imgHeight = height;
      this.canHeight = (height / width) * this.canWidth;
      setTimeout(() => {
        this.originCtx.clearRect(0, 0, this.canWidth, this.canHeight);
        this.originCtx.scale(this.canWidth / width, this.canWidth / width);
        this.originCtx.drawImage(img, 0, 0);

        this.resetImgData();
      }, 0);
    },
    addStock(params) {
      if (this.index) {
        this.imgStock = this.imgStock.slice(this.index);
        this.index = 0;
      }
      if (this.imgStock.length >= 10) {
        this.imgStock.pop();
      }
      this.imgStock.unshift(JSON.stringify(params));
    },
    undo() {
      this.index++;
      this.redrawImg();
    },
    redo() {
      this.index--;
      this.redrawImg();
    },
    redrawImg() {
      const preImageData = JSON.parse(this.imgStock[this.index]).data;
      this.imageData = this.transCtx.createImageData(
        this.canWidth,
        this.canHeight
      );
      for (let i = 0; i < this.imageData.data.length; i++) {
        this.imageData.data[i] = preImageData[i];
      }
      this.transCtx.putImageData(this.imageData, 0, 0);
    },
    // 图片数据转化
    handleTrans(
      mode = "replace",
      _fromColor = this.selectedColor.from,
      toColor = this.transColor,
      stock = true
    ) {
      const _toColor = mode === "remove" ? [0, 0, 0, 0] : toColor;
      const data = this.imageData.data || [];
      console.time("no-worker");
      for (let i = 0; i < data.length; i += 4) {
        const similar = this.isSimilar(_fromColor, data.slice(i, i + 4));
        if (similar) {
          data[i] = _toColor[0];
          data[i + 1] = _toColor[1];
          data[i + 2] = _toColor[2];
          _toColor[3] && (data[i + 3] = _toColor[3] * 255);
        }
      }
      console.timeEnd("no-worker");
      stock && this.addStock(this.imageData);
      this.transCtx.putImageData(this.imageData, 0, 0);
      this.imageData = this.transCtx.getImageData(
        0,
        0,
        this.canWidth,
        this.canHeight
      );
    },
    // 点击位置的颜色值
    pickPoint(e, ref) {
      const target = this.strawPoint ? "to" : "from";
      this.selectedColor[target] = Array.from(
        this[ref + "Ctx"].getImageData(e.layerX, e.layerY, 1, 1).data
      );
      const _color = this.getOpposite(this.selectedColor[target]);
      this.canWidth = this.canWidth + 0;
      this.layerCtx.strokeStyle = _color;
      this.layerCtx.clearRect(0, 0, this.canWidth, this.canHeight);
      this.layerCtx.strokeRect(e.layerX - 3, e.layerY - 3, 6, 6);
    },
    // 反色
    getOpposite(color) {
      const res = [];
      for (let i = 0; i < 3; i++) {
        res.push(255 - color[i]);
      }
      res.push(255);
      return `rgba(${res.join(",")})`;
    },
    // 相似颜色域
    isSimilar(arr1, arr2) {
      if (arr1.length !== arr2.length) return false;
      const distance = this.colourDistance(arr1, arr2);
      if (distance > this.tolerance) {
        return false;
      }
      return true;
    },
    // 颜色距离表示相似度：[0, 3*255]
    colourDistance(arr1, arr2) {
      const [r1, g1, b1] = arr1;
      const [r2, g2, b2] = arr2;
      const rmean = (r1 + r2) / 2;
      const r = r1 - r2;
      const g = g1 - g2;
      const b = b1 - b2;
      return Math.sqrt(
        (2 + rmean / 256) * r ** 2 +
          4 * g ** 2 +
          (2 + (255 - rmean) / 256) * b ** 2
      );
    },

    handleStatusChange(file) {
      this.fileName = file.name;
      this.originImg = URL.createObjectURL(file.raw);
    },

    downloadPng() {
      const imgData = this.$refs.transCan.toDataURL("image/png");
      const save_link = document.createElement("a");
      save_link.href = imgData;
      save_link.download = this.fileName + ".png";
      save_link.click();
    },
    resetImgData() {
      this.imgStock = [];
      this.index = 0;
      this.selectedColor = this.$options.data().selectedColor;
      this.imageData = this.originCtx.getImageData(
        0,
        0,
        this.canWidth,
        this.canHeight
      );
      this.transCtx.clearRect(0, 0, this.canWidth, this.canHeight);
      this.addStock(this.imageData);
    },
    uploadImg(img) {
      // const image = new Image()
      // image.src = img
      // console.log(URL.createObjectURL(image))

      this.fileName = img.split("/").pop();
      this.originImg = img;
    },
  },
};
</script>

<style scoped lang="scss">
.main {
  width: 100%;
  position: absolute;
  left: 0;
  top: 0;
  padding: 12px;
  background: #fff;
}

.header-content {
  padding: 6px 18px 12px;
}
.el-col {
  padding: 0 6px 6px;
}
.canvas-content {
  position: relative;
  background: #ffffff;
  overflow: hidden;
  canvas {
    cursor: pointer;
  }
  .origin-box {
    border: 1px solid #f56c6c;
    .origin-canvas-content {
      position: relative;
      margin: 0 auto;
    }
    #layer {
      position: absolute;
      left: 0;
      top: 0;
      z-index: 9;
    }
  }
  .trans-box {
    border: 1px solid #67c23a;
    .trans-canvas-content {
      position: relative;
      margin: 0 auto;
    }
  }
  .operate-list-box {
    display: flex;
    flex-direction: column;
    flex: 170px 0 0;
    text-align: left;
    padding: 6px 24px 18px;
    justify-content: center;
    .operate-item {
      .header {
        margin: 18px 0 9px;
      }
      .body {
        padding-bottom: 12px;
      }
    }
    .replace-btn {
      vertical-align: top;
      margin: 0 6px;
    }
    .replace-color-picker-box {
      display: flex;
      align-items: center;
      .straw {
        color: #909399;
        &.active {
          color: #409eff;
        }
      }
    }
  }
}
#imgDemo {
  position: absolute;
  top: 0;
  left: 0;
}
.iconfont {
  font-size: 20px;
}
.example-content {
  display: flex;
  justify-content: center;
  padding: 30px;
  .label {
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  img {
    border-radius: 6px;
    margin-right: 9px;
    cursor: pointer;
  }
}
/deep/ .el-step__title {
  font-size: 12px;
}
.tooltip {
  font-size: 12px;
  color: #909399;
  /deep/ .el-button {
    &.active {
    }
  }
}
</style>
